#include "boost/date_time/posix_time/posix_time.hpp"
#include "enet/enet.h"
#include <cstdio>
#include <cstdlib>
#include <string>
#include <sstream>
#include <algorithm>

#include "vector3.h"
#include "quaternion.h"
#include "networkNode.h"

using namespace sde;

class Server : public NetworkNode {
public:
    typedef std::list<ENetPeer*> PeerList;
    typedef std::map<std::string, ENetPeer*> StringPeerMap;
    typedef std::map<std::string, PeerList> StringPeerListMap;
    typedef std::map<std::string, VarMap> StateMap;
    typedef std::map<std::string, int> StringIntMap;
    
    StringPeerListMap varSetters;
    StringPeerListMap varGetters;
    
    StateMap state;
    PeerList clients;
    StringPeerListMap modulePeers;
    StringIntMap actionCounts;
    StringIntMap moduleRunCounts;
    StringPeerMap rpcPeers;
    StringPeerListMap rpcRequestPeers;
    float startTime;
    int numBytesReceived;

    Server() : NetworkNode(true) {
        startTime = getTime();
        numBytesReceived = 0;
    }
    
    float getTime() {
        return float(boost::posix_time::microsec_clock::universal_time()
                .time_of_day().total_nanoseconds()) / 1000000000.0;
    }
    
    void onConnected(ENetPeer* peer) {
        printf("got client\n");
        
        clients.push_back(peer);
        
        sendState(peer);
    }
    
    void sendState(ENetPeer* peer) {
        ENetPacket* packet = createPacket();
        writeChar(packet,initStateCmd);
        writeInt(packet,state.size());
        for (StateMap::iterator i = state.begin(); i != state.end(); ++i) {
            writeString(packet,i->first);
            writeVarMap(packet,i->second);
        }
        send(peer,packet);
    }
    
    void onDisconnected(ENetPeer* peer) {
        printf("lost client\n");
        
        clients.remove(peer);
        
        for (StringPeerListMap::iterator i = modulePeers.begin(); i != modulePeers.end(); ++i)
            i->second.remove(peer);
    }
    
    void receiveRegisterSetVar(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string varId = readString(packet,pos);
        
        if (varSetters.find(varId) == varSetters.end())
            varSetters[varId] = PeerList();
        varSetters[varId].remove(peer);
        varSetters[varId].push_back(peer);
        
        if (varGetters.find(varId) != varGetters.end()) {
            bool anyNotPeer = false;
            
            for (PeerList::iterator i = varGetters[varId].begin(); i != varGetters[varId].end(); ++i)
                if (*i != peer)
                    anyNotPeer = true;
                    
            if (anyNotPeer) {
                ENetPacket* registerPacket = createPacket();
                writeChar(registerPacket,registerSetVarCmd);
                writeString(registerPacket,varId);
                send(peer,registerPacket);
            }
        }
    }
    
    void receiveDistSetVar(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string objectName = readString(packet,pos);
        std::string varName = readString(packet,pos);
        Var val = readVar(packet,pos);
        
        if (state.find(objectName) == state.end())
            printf("ERROR: can't set var %s in unknown object %s\n",varName.c_str(),objectName.c_str());
        else {
            state[objectName][varName] = val;
        
            for (PeerList::iterator i = clients.begin(); i != clients.end(); ++i)
                if (*i != peer) {
                    ENetPacket* replyPacket = createPacket();
                    writeChar(replyPacket,distSetVarCmd);
                    writeString(replyPacket,objectName);
                    writeString(replyPacket,varName);
                    writeVar(replyPacket,val);
                    send(*i,replyPacket);
                }
        }
    }
        
    void receiveSetVar(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string objectName = readString(packet,pos);
        std::string varName = readString(packet,pos);
        Var val = readVar(packet,pos);
        
        if (state.find(objectName) == state.end()) {
            printf("ERROR: can't set var %s in unknown object %s\n",varName.c_str(),objectName.c_str());
        } else {
            state[objectName][varName] = val;
        
            std::string varId = objectName + std::string(".") + varName;
            
            
/*            bool firstSet = (state[objectName].find(varName) == state[objectName].end());
            state[objectName][varName] = val;
            if (!firstSet) {
                if (varSetters.find(varId) == varSetters.end())
                    varSetters[varId] = peer;
                else if (varSetters[varId] != peer) {
                    printf("ERROR: race condition detected on %s\n",varId.c_str());
                    //return;
                }
            } else if (varSetters.find(varId) != varSetters.end()) {
                varSetters.erase(varId);
            }*/
            
            if (varGetters.find(varId) != varGetters.end()) {
                
                int setCount = 0;
                
                for (PeerList::iterator i = varGetters[varId].begin(); i != varGetters[varId].end(); ++i)
                    if (*i != peer) {
                        setCount++;
                        send(*i,packet);
                    }
            
                if (setCount) {
//                     printf("%d varGetters for %s\n",setCount,varId.c_str());
                }
            }
        }
    }
    
    void receiveRequestVar(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string objectName = readString(packet,pos);
        std::string varName = readString(packet,pos);
        
        std::string varId = objectName + std::string(".") + varName;
        
        printf("register getter for %s\n",varId.c_str());
        
        if (varSetters.find(varId) != varSetters.end()) {
            for (PeerList::iterator i = varSetters[varId].begin(); i != varSetters[varId].end(); ++i) {
                if (*i != peer) {
                    printf("notify setter that %s is requested\n",varId.c_str());
                    ENetPacket* registerPacket = createPacket();
                    writeChar(registerPacket,registerSetVarCmd);
                    writeString(registerPacket,varId);
                    send(*i,registerPacket);
                }
            }
        }
    
        if (varGetters.find(varId) == varGetters.end())
            varGetters[varId] = PeerList();
        
        varGetters[varId].remove(peer);
        varGetters[varId].push_back(peer);
        
        if (state.find(objectName) != state.end() && state[objectName].find(varName) != state[objectName].end()) {
            printf("varGetter registered for already set var %s\n",varId.c_str());
            ENetPacket* packet = createPacket();
            writeChar(packet,setVarCmd);
            writeString(packet,objectName);
            writeString(packet,varName);
            writeVar(packet,state[objectName][varName]);
            send(peer,packet);
        }
    
//         printf("add var getter for %s\n",varId.c_str());
    }
    
    void receiveModules(ENetPeer* peer, ENetPacket* packet, int& pos) {
        int numModules = readInt(packet,pos);
        printf("receive %d modules\n",numModules);
        for (int i = 0; i < numModules; ++i) {
            std::string module = readString(packet,pos);
            printf("    %s\n",module.c_str());
            if (modulePeers.find(module) == modulePeers.end())
                modulePeers[module] = PeerList();
            modulePeers[module].push_back(peer);
        }
    }
    
    void receiveMakeObjectName(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string desiredName = readString(packet,pos);
        std::string name;
        if (state.find(desiredName) == state.end()) {
            name = desiredName;
        } else {
            std::ostringstream sout;
            int i = 2;
            do {
                sout << desiredName << "_" << i++;
                name = sout.str();
                sout.str("");
            } while (state.find(name) != state.end());
        }
        
        printf("make object %s\n",name.c_str());
        
        state[name] = VarMap();
        
        ENetPacket* replyPacket = createPacket();
        writeChar(replyPacket,objectNameCmd);
        writeString(replyPacket,name);
        send(peer,replyPacket);
        
        for (PeerList::iterator i = clients.begin(); i != clients.end(); ++i)
            if (*i != peer) {
                ENetPacket* createObjectPacket = createPacket();
                writeChar(createObjectPacket,makeObjectCmd);
                writeString(createObjectPacket,name);
                send(*i,createObjectPacket);
            }
    }
    
    void receiveDestroyObject(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string objectName = readString(packet,pos);
        if (state.find(objectName) != state.end()) {
            printf("delete object %s\n",objectName.c_str());
        
            state.erase(objectName);
        
            for (PeerList::iterator i = clients.begin(); i != clients.end(); ++i)
                if (*i != peer)
                    send(*i,packet);
        }
    }
    
    std::string makeActionName(std::string desiredName) {
        std::ostringstream sout;
        sout << desiredName;
        if (actionCounts.find(desiredName) == actionCounts.end())
            actionCounts[desiredName] = 0;
        else
            sout << "_" << actionCounts[desiredName];
        actionCounts[desiredName]++;
        return sout.str();
    }
    
    void receiveRunAction(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string objectName = readString(packet,pos);
        std::string actionType = readString(packet,pos);
        std::string actionName = makeActionName(readString(packet,pos));
        
        ENetPacket* namePacket = createPacket();
        writeChar(namePacket,actionNameCmd);
        writeString(namePacket,actionName);
        send(peer,namePacket);
        
        std::string module = actionType.substr(0,actionType.find("/"));
        
        if (modulePeers.find(module) == modulePeers.end()) {
            printf("ERROR: nobody runs module %s\n",module.c_str());
        } else {
            if (moduleRunCounts.find(module) == moduleRunCounts.end())
                moduleRunCounts[module] = 0;
            int pos = moduleRunCounts[module]++ % modulePeers[module].size();
            PeerList::const_iterator i = modulePeers[module].begin();
            std::advance(i,pos);
            ENetPeer* runPeer = *i;
            
            ENetPacket* runPacket = createPacket();
            writeChar(runPacket,runActionCmd);
            writeString(runPacket,objectName);
            writeString(runPacket,actionType);
            writeString(runPacket,actionName);
            send(runPeer,runPacket);
        }
    }
    
    void receiveSubscribeRpc(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string objectName = readString(packet,pos);
        std::string cmd = readString(packet,pos);
        std::string rpcId = objectName + "." + cmd;
        
        rpcPeers[rpcId] = peer;
    }
    
    void receiveRpc(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string objectName = readString(packet,pos);
        std::string cmd = readString(packet,pos);
        VarMap args = readVarMap(packet,pos);
        std::string rpcId = objectName + "." + cmd;
        
        if (rpcPeers.find(rpcId) == rpcPeers.end()) {
            printf("ERROR: no clients registered to run rpc %s on object %s\n",
                cmd.c_str(),objectName.c_str());
        } else {
            if (rpcRequestPeers.find(rpcId) == rpcRequestPeers.end())
                rpcRequestPeers[rpcId] = PeerList();
            rpcRequestPeers[rpcId].push_back(peer);
        
            send(rpcPeers[rpcId],packet);
        }
    }
    
    void receiveRpcResult(ENetPeer* peer, ENetPacket* packet, int& pos) {
        std::string rpcId = readString(packet,pos) + "." + readString(packet,pos);
        Var result = readVar(packet,pos);
        
        if (rpcRequestPeers.find(rpcId) != rpcRequestPeers.end() && !rpcRequestPeers[rpcId].empty()) {
            ENetPeer* requestPeer = *rpcRequestPeers[rpcId].begin();
            rpcRequestPeers[rpcId].pop_front();
            
            send(requestPeer,packet);
        }
    }
    
    void receive(ENetPeer* peer, ENetPacket* packet) {
        int pos = 0;
        unsigned char cmd = readChar(packet,pos);
        switch (cmd) {
        case modulesCmd:
            receiveModules(peer,packet,pos);
            break;
        case makeObjectNameCmd:
            receiveMakeObjectName(peer,packet,pos);
            break;
        case setVarCmd:
            receiveSetVar(peer,packet,pos);
            break;
        case distSetVarCmd:
            receiveDistSetVar(peer,packet,pos);
            break;
        case requestVarCmd:
            receiveRequestVar(peer,packet,pos);
            break;
        case destroyObjectCmd:
            receiveDestroyObject(peer,packet,pos);
            break;
        case runActionCmd:
            receiveRunAction(peer,packet,pos);
            break;
        case subscribeRpcCmd:
            receiveSubscribeRpc(peer,packet,pos);
            break;
        case rpcCmd:
            receiveRpc(peer,packet,pos);
            break;
        case rpcResultCmd:
            receiveRpcResult(peer,packet,pos);
            break;
        case registerSetVarCmd:
            receiveRegisterSetVar(peer,packet,pos);
            break;
        }
        
        numBytesReceived += pos;
        float time = getTime(), dtime = time - startTime;
        if (dtime > 1) {
            printf("%f\n",float(numBytesReceived)/dtime);
            startTime = time;
            numBytesReceived = 0;
        }
    }
};

int main() {
    Server server;
    
    server.connect("localhost",1234);
    while (true) server.pump(1000);
    
    return 0;
}
